<?php

namespace pmvc\data\sql;

use pmvc\util\ReflectionUtil;

use Exception;
use DateTime;

/**
 * Provides cross-platform functionality for building queries
 * and basic implementations of methos from {@link DataSource}.
 *
 */
abstract class AbstractDataSource 
	implements DataSource {

	/**
	 * Builds a query with the given arguments.
	 * @param string $sql
	 * @param array $args
	 * @throws Exception on error
	 */
	public function buildQuery($sql, $args) {
		if (!isset($args) || empty($args) || count($args)==0) {
			return $sql;
		}
		$ds = $this;
		return preg_replace_callback('/\{([0-9A-Za-z\._\-]+)\}/',
			function($matches)use($args, $ds) {
				$key 	= $matches[1];
				$ret 	= null;
		    	if (is_null($args[$key]) || !isset($args[$key])) {
		    		$ret = "NULL";
		    	} else if (is_string($args[$key])) {
		    		$ret = "'".$ds->escape($args[$key])."'";
		    	} else if (is_numeric($args[$key])) {
		    		$ret = $args[$key];
		    	} else if (is_bool($args[$key])) {
		    		$ret = $args[$key]===TRUE ? 1 : 0;
		    	} else if (is_object($args[$key]) && ReflectionUtil::isInstance($args[$key], 'DateTime')) {
		    		$ret = "'".$args[$key]->format("Y-m-d H:i:s")."'";
		    	} else if (is_array($args[$key])) {
		    		$sqlTmp = "";
		    		for ($i=0; $i<count($args[$key]); $i++) {
		    			$sqlTmp .= "{".$i."}";
		    			if ($i<(count($args[$key])-1)) {
		    				$sqlTmp .= ",";
		    			}
		    		}
		    		$ret = $ds->buildQuery($sqlTmp, $args[$key]);
		    	} else {
		    		throw new Exception(
		    			"Unable to determine variable type for key ".$key." of sql query");
		    	}
				return $ret;
			}, $sql);
	}

	public function queryParams($sql, $params) {
		return call_user_func(array($this, "query"), $this->buildQuery($sql, $params));
	}

	public function updateParams($sql, $params) {
		return call_user_func(array($this, "update"), $this->buildQuery($sql, $params));
	}
	
	public function querySingle() {
		$a = func_get_args();
        $res = call_user_func_array(array($this,"query"), $a);
        $numRows = $this->numberOfRows($res);
        $ret = ($numRows>0 || $numRows==-1)
        	? $this->fetchRowObject($res)
        	: false;
        $this->freeResult($res);
        return $ret;
	}

	public function querySingleParams($sql, $params) {
		return call_user_func(array($this, "querySingle"), $this->buildQuery($sql, $params));
	}
	
	public function queryMultiple() {
		$a = func_get_args();
        $res = call_user_func_array(array($this,"query"), $a);
        $numRows = $this->numberOfRows($res);
        $ret = ($numRows>0 || $numRows==-1)
        	? $this->fetchRowObjects($res)
        	: Array();
        $this->freeResult($res);
        return $ret;
	}

	public function queryMultipleParams($sql, $params) {
		return call_user_func(array($this, "queryMultiple"), $this->buildQuery($sql, $params));
	}
	
	public function fetchRowObjects($res) {
		$objs = Array();
		while (($obj = $this->fetchRowObject($res))!==FALSE) {
			$objs[] = $obj;
		}
		return $objs;
	}

}

?>